package main

import (
	"crypto/tls"
	"crypto/x509"
	"github.com/gin-gonic/gin"
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"
	"github.com/spf13/pflag"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"io"
	"io/ioutil"
	"zimuzu_cvn_web_api/lib/grpcClient"
	"zimuzu_cvn_web_api/service/cron"

	"os"
	"zimuzu_cvn_web_api/config"
	"zimuzu_cvn_web_api/pkg/drive/db"
	"zimuzu_cvn_web_api/pkg/drive/redis"
	"zimuzu_cvn_web_api/router"
)

var (
	cfg     = pflag.StringP("config", "c", "", "api server config file path.")
	version = pflag.BoolP("version", "v", false, "show version info.")
)

var tasks = []func() error{
	setLogLevel,
	setRedisPool,
	setMySQLConnection,
	//runDatabaseMigrations,
	//setRootAccount,
	setRouter,
	//startRouteWebsock,
	setAPIs,
	setRpc,
	setCron,
	startServer,
}

func main() {
	// init config
	if err := config.Init(*cfg); err != nil {
		panic(err)
	}

	// start services
	start()
	//gracehttp.Serve(
	//	&http.Server{Addr: ":8080", Handler: config.C.Router},
	//	&http.Server{Addr: ":8081", Handler: config.C.Router},
	//)
}

func start() error {
	log.Infoln("Service start")

	for _, t := range tasks {
		if err := t(); err != nil {
			log.Fatal(err)
		}
	}

	return nil
}

func setLogLevel() error {
	if config.C.Log.LoggerType == "json" {
		log.SetFormatter(&log.JSONFormatter{})
	} else {
		log.SetFormatter(&log.TextFormatter{
			TimestampFormat: "2006-01-02 15:04:05",
			DisableSorting:  true,
		})
	}
	log.SetOutput(os.Stdout)
	log.SetLevel(log.Level(config.C.Log.LoggerLevel))

	return nil
}

func setRedisPool() error {
	log.WithField("url", config.C.Redis.URL).Info("setup redis connection pool")
	err := redis.InitPool(config.C.Redis.URL, config.C.Redis.Password)
	return err
}

func setMySQLConnection() error {
	log.WithField("DB type", config.C.DB.Type).WithField("dsn", config.C.DB.DSN).Info("connecting to DB")
	DbConn, err := db.OpenDatabase(config.C.DB.Type, config.C.DB.DSN)
	if err != nil {
		return errors.Wrap(err, "database connection error")
	}
	config.C.DB.DB = DbConn
	return nil
}

func setRouter() error {
	if config.C.RunMode == "DEBUG" {
		gin.SetMode(gin.DebugMode)
	} else {
		gin.SetMode(gin.ReleaseMode)
	}

	gin.DisableConsoleColor()

	// Logging to a file.
	if config.C.Log.LoggerFile != "" {
		f, _ := os.Create(config.C.Log.LoggerFile)
		gin.DefaultWriter = io.MultiWriter(f, os.Stdout)
	} else {
		gin.DefaultWriter = io.MultiWriter(os.Stdout)
	}
	config.C.Router = gin.New()
	config.C.Router.Use(gin.Recovery())

	return nil
}

func setAPIs() error {
	api.BindAPIs()
	return nil
}

func startServer() error {
	config.C.Router.Run(config.C.Address)

	// go func() {
	// 	config.C.Router.Run(config.C.Address)
	// }()
	return nil
}

func setRpc() error {
	cert, err := tls.LoadX509KeyPair("conf/certificate/client.pem", "conf/certificate/client.key")
	if err != nil {
		log.Fatalf("tls.LoadX509KeyPair err: %v", err)
	}

	certPool := x509.NewCertPool()
	ca, err := ioutil.ReadFile("conf/certificate/ca.pem")
	if err != nil {
		log.Fatalf("ioutil.ReadFile err: %v", err)
	}

	if ok := certPool.AppendCertsFromPEM(ca); !ok {
		log.Fatalf("certPool.AppendCertsFromPEM err")
	}

	c := credentials.NewTLS(&tls.Config{
		Certificates: []tls.Certificate{cert},
		ServerName:   "cvnt",
		RootCAs:      certPool,
	})

	conn, err := grpc.Dial(config.C.Rpc.Url, grpc.WithTransportCredentials(c))
	if err != nil {
		return errors.Wrap(err, "grpc dial error")
	}
	config.C.Rpc.Conn = conn
	grpcClient.InitClient(config.C.Rpc.Conn)
	return nil
}

func setCron() error {
	return cron.Init()
}
